﻿using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Drawing;
using System.Data;
using System.Text;
using System.Windows.Forms;
using Tao.OpenGl;
using Tao.Platform.Windows;
using System.Runtime.InteropServices;
using System.Diagnostics;

namespace TaoGL_UserControl
{
    public partial class GLControl : UserControl
    {

        private IntPtr hDC;                                              // Private GDI Device Context
        private IntPtr hRC;                                              // Permanent Rendering Context

        Timer TimerRun;

        public GLControl()
        {
            InitializeComponent();
            this.CreateParams.ClassStyle = this.CreateParams.ClassStyle |       // Redraw On Size, And Own DC For Window.
User.CS_HREDRAW | User.CS_VREDRAW | User.CS_OWNDC;
            this.SetStyle(ControlStyles.AllPaintingInWmPaint, true);            // No Need To Erase Form Background
            this.SetStyle(ControlStyles.DoubleBuffer, true);                    // Buffer Control
            this.SetStyle(ControlStyles.Opaque, true);                          // No Need To Draw Form Background
            this.SetStyle(ControlStyles.ResizeRedraw, true);                    // Redraw On Resize
            this.SetStyle(ControlStyles.UserPaint, true);                       // We'll Handle Painting Ourselves

            CreateGLWindow();


            TimerRun = new Timer();
            TimerRun.Interval = 16;
            TimerRun.Tick += new EventHandler(TimerRun_Tick);
            TimerRun.Start();


        }
        //======================================================================
        void TimerRun_Tick(object sender, EventArgs e)
        {
            DrawGLScene();

        }

        //======================================================================
        public bool CreateGLWindow()
        {
            GC.Collect();                                                       // Request A Collection
            // This Forces A Swap
            Kernel.SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1);


            int pixelFormat;
            Gdi.PIXELFORMATDESCRIPTOR pfd = new Gdi.PIXELFORMATDESCRIPTOR();    // pfd Tells Windows How We Want Things To Be
            pfd.nSize = (short)Marshal.SizeOf(pfd);                            // Size Of This Pixel Format Descriptor
            pfd.nVersion = 1;                                                   // Version Number
            pfd.dwFlags = Gdi.PFD_DRAW_TO_WINDOW |                              // Format Must Support Window
                Gdi.PFD_SUPPORT_OPENGL |                                        // Format Must Support OpenGL
                Gdi.PFD_DOUBLEBUFFER;                                           // Format Must Support Double Buffering
            pfd.iPixelType = (byte)Gdi.PFD_TYPE_RGBA;                          // Request An RGBA Format
            pfd.cColorBits = (byte)16;                                       // Select Our Color Depth
            pfd.cRedBits = 0;                                                   // Color Bits Ignored
            pfd.cRedShift = 0;
            pfd.cGreenBits = 0;
            pfd.cGreenShift = 0;
            pfd.cBlueBits = 0;
            pfd.cBlueShift = 0;
            pfd.cAlphaBits = 0;                                                 // No Alpha Buffer
            pfd.cAlphaShift = 0;                                                // Shift Bit Ignored
            pfd.cAccumBits = 0;                                                 // No Accumulation Buffer
            pfd.cAccumRedBits = 0;                                              // Accumulation Bits Ignored
            pfd.cAccumGreenBits = 0;
            pfd.cAccumBlueBits = 0;
            pfd.cAccumAlphaBits = 0;
            pfd.cDepthBits = 16;                                                // 16Bit Z-Buffer (Depth Buffer)
            pfd.cStencilBits = 0;                                               // No Stencil Buffer
            pfd.cAuxBuffers = 0;                                                // No Auxiliary Buffer
            pfd.iLayerType = (byte)Gdi.PFD_MAIN_PLANE;                         // Main Drawing Layer
            pfd.bReserved = 0;                                                  // Reserved
            pfd.dwLayerMask = 0;                                                // Layer Masks Ignored
            pfd.dwVisibleMask = 0;
            pfd.dwDamageMask = 0;



            hDC = User.GetDC(this.Handle);

            if (hDC == IntPtr.Zero)
            {                                            // Did We Get A Device Context?
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Can't Create A GL Device Context.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }


            pixelFormat = Gdi.ChoosePixelFormat(hDC, ref pfd);                  // Attempt To Find An Appropriate Pixel Format
            if (pixelFormat == 0)
            {                                              // Did Windows Find A Matching Pixel Format?
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Can't Find A Suitable PixelFormat.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            if (!Gdi.SetPixelFormat(hDC, pixelFormat, ref pfd))
            {                // Are We Able To Set The Pixel Format?
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Can't Set The PixelFormat.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            hRC = Wgl.wglCreateContext(hDC);                                    // Attempt To Get The Rendering Context
            if (hRC == IntPtr.Zero)
            {                                            // Are We Able To Get A Rendering Context?
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Can't Create A GL Rendering Context.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            if (!Wgl.wglMakeCurrent(hDC, hRC))
            {                                 // Try To Activate The Rendering Context
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Can't Activate The GL Rendering Context.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            ReSizeGLScene(this.Width, this.Height);
            if (!InitGL())
            {                                                     // Initialize Our Newly Created GL Window
                KillGLWindow();                                                 // Reset The Display
                MessageBox.Show("Initialization Failed.", "ERROR",
                    MessageBoxButtons.OK, MessageBoxIcon.Error);
                return false;
            }

            return true;
        }
        //======================================================================
        #region KillGLWindow()
        /// <summary>
        ///     Properly kill the window.
        /// </summary>
        private void KillGLWindow()
        {

            if (hRC != IntPtr.Zero)
            {                                            // Do We Have A Rendering Context?
                if (!Wgl.wglMakeCurrent(IntPtr.Zero, IntPtr.Zero))
                {             // Are We Able To Release The DC and RC Contexts?
                    MessageBox.Show("Release Of DC And RC Failed.", "SHUTDOWN ERROR",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                if (!Wgl.wglDeleteContext(hRC))
                {                                // Are We Able To Delete The RC?
                    MessageBox.Show("Release Rendering Context Failed.", "SHUTDOWN ERROR",
                        MessageBoxButtons.OK, MessageBoxIcon.Error);
                }

                hRC = IntPtr.Zero;                                              // Set RC To Null
            }

            if (hDC != IntPtr.Zero)
            {                                            // Do We Have A Device Context?
                if (this != null && !this.IsDisposed)
                {                          // Do We Have A Window?
                    if (this.Handle != IntPtr.Zero)
                    {                            // Do We Have A Window Handle?
                        if (!User.ReleaseDC(this.Handle, hDC))
                        {                 // Are We Able To Release The DC?
                            MessageBox.Show("Release Device Context Failed.", "SHUTDOWN ERROR",
                                MessageBoxButtons.OK, MessageBoxIcon.Error);
                        }
                    }
                }

                hDC = IntPtr.Zero;                                              // Set DC To Null
            }
        }
        #endregion KillGLWindow()

        //======================================================================

        //======================================================================
        private static bool InitGL()
        {
            /*
            Gl.glEnable(Gl.GL_TEXTURE_2D);
            Gl.glShadeModel(Gl.GL_SMOOTH);
            //Gl.glEnable(Gl.GL_DEPTH_TEST);    //使用 Blend 必須要將 DEPTH_TEST關閉(我想在2D的情況用不到這個指令)
            Gl.glEnable(Gl.GL_BLEND);
            Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE_MINUS_SRC_ALPHA);
            //Gl.glBlendFunc(Gl.GL_SRC_ALPHA, Gl.GL_ONE);
            Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);
            //----------------------------------------------------------------------

            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            Gl.glOrtho(0.0, 1366, 0.0, 768, -1000.0, 1000.0);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
             */

            Gl.glShadeModel(Gl.GL_SMOOTH);                                      // Enable Smooth Shading
            Gl.glClearColor(0, 0, 0, 0.5f);                                     // Black Background
            Gl.glClearDepth(1);                                                 // Depth Buffer Setup
            Gl.glEnable(Gl.GL_DEPTH_TEST);                                      // Enables Depth Testing
            Gl.glDepthFunc(Gl.GL_LEQUAL);                                       // The Type Of Depth Testing To Do
            Gl.glHint(Gl.GL_PERSPECTIVE_CORRECTION_HINT, Gl.GL_NICEST);         // Really Nice Perspective Calculations
            return true;
        }

        //======================================================================
        private static float rtri;                                              // Angle For The Triangle ( NEW )
        private static float rquad;     
        private bool DrawGLScene()
        {


            Gl.glClear(Gl.GL_COLOR_BUFFER_BIT | Gl.GL_DEPTH_BUFFER_BIT);        // Clear Screen And Depth Buffer
            Gl.glLoadIdentity();                                                // Reset The Current Modelview Matrix

            Gl.glTranslatef(-1.5f, 0, -6);                                      // Move Left 1.5 Units And Into The Screen 6.0
            Gl.glRotatef(rtri, 0, 1, 0);                                        // Rotate The Triangle On The Y axis ( NEW )
            Gl.glBegin(Gl.GL_TRIANGLES);                                        // Drawing Using Triangles
            Gl.glColor3f(1, 0, 0);                                          // Set The Color To Red
            Gl.glVertex3f(0, 1, 0);                                         // Top
            Gl.glColor3f(0, 1, 0);                                          // Set The Color To Green
            Gl.glVertex3f(-1, -1, 0);                                       // Bottom Left
            Gl.glColor3f(0, 0, 1);                                          // Set The Color To Blue
            Gl.glVertex3f(1, -1, 0);                                        // Bottom Right
            Gl.glEnd();                                                         // Finished Drawing The Triangle
            Gl.glLoadIdentity();                                                // Reset The Current Modelview Matrix
            Gl.glTranslatef(1.5f, 0, -6);                                       // Move Right 1.5 Units And Into The Screen 6.0
            Gl.glRotatef(rquad, 1, 0, 0);                                       // Rotate The Quad On The X axis ( NEW )
            Gl.glColor3f(0.5f, 0.5f, 1);                                        // Set The Color To Blue One Time Only
            Gl.glBegin(Gl.GL_QUADS);                                            // Draw A Quad
            Gl.glVertex3f(-1, 1, 0);                                        // Top Left
            Gl.glVertex3f(1, 1, 0);                                         // Top Right
            Gl.glVertex3f(1, -1, 0);                                        // Bottom Right
            Gl.glVertex3f(-1, -1, 0);                                       // Bottom Left
            Gl.glEnd();                                                         // Done Drawing The Quad
            rtri += 2.0f;                                                       // Increase The Rotation Variable For The Triangle ( NEW )
            rquad -= 1.5f;         





            Gdi.SwapBuffers(hDC);

            return true;
        }

        //======================================================================
        private void ReSizeGLScene(int width, int height)
        {

            if (height == 0)
            {                                                
                height = 1;                                                   
            }        

            Gl.glViewport(0, 0, width, height);                                
            Gl.glMatrixMode(Gl.GL_PROJECTION);
            Gl.glLoadIdentity();
            //Gl.glOrtho(0.0, 1366, 0.0, 768, -1000.0, 1000.0);
            Glu.gluPerspective(45, width / (double)height, 0.1, 100);
            Gl.glMatrixMode(Gl.GL_MODELVIEW);
            Gl.glLoadIdentity();
    



        }

        private void GLControl_Resize(object sender, EventArgs e)
        {
            ReSizeGLScene(this.Width, this.Height);
        }


    }
}
